package demo.guiJFrame;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class Game2048_v3 {
	JFrame frame;
	JPanel record, panel;
	JButton button;
	JLabel count, time, maxCount;
	Grid3[] Grids;
	int[][] num;
	boolean ifCanMove = true;
	int maxCo = 0, nowCount = 0, oldCo = 0, minute = 0, second = 0;
	TimeThread tt;
	Linked link = new Linked();
	int[][] oldNum;

	class TimeThread extends Thread {
		int a = 1;

		public void stopp() {
			a = 0;
		}

		public void run() {
			while (ifCanMove && a == 1) {
				try {
					if (second > 59) {
						second = 0;
						minute++;
					}
					time.setText("��ǰ��ʱ " + minute + ":" + second++);
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		}
	}

	public Game2048_v3() {
		setNum();
		frame = new JFrame("WEY2048");
		frame.setBounds(750, 300, 400, 465);
		frame.setLayout(new BorderLayout());
		panel = new JPanel();
		panel.setSize(400, 400);
		panel.setLayout(new GridLayout(4, 4, 1, 1));
		record = new JPanel();
		record.setSize(400, 100);
		record.setLayout(new GridLayout(2, 2, 1, 1));
		Grids = new Grid3[16];
		for (int i = 0; i < 16; i++) {
			Grids[i] = new Grid3(num[i / 4][i % 4]);
			panel.add(Grids[i].ret());
		}
		button = new JButton("����Ϸ");
		maxCount = new JLabel("��߷�" + maxCo);
		maxCount.setHorizontalAlignment(JLabel.CENTER);
		maxCount.setFont(new Font("����", Font.BOLD, 16));
		count = new JLabel("��ǰ���� " + nowCount);
		count.setHorizontalAlignment(JLabel.CENTER);
		count.setFont(new Font("����", Font.BOLD, 16));
		time = new JLabel("��ǰ��ʱ " + minute + ":" + second);
		time.setHorizontalAlignment(JLabel.CENTER);
		time.setFont(new Font("����", Font.BOLD, 16));
		addLis();
		record.add(count);
		record.add(maxCount);
		record.add(time);
		record.add(button);
		frame.add(record, BorderLayout.NORTH);
		frame.add(panel, BorderLayout.CENTER);
		frame.setResizable(false);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
		panel.requestFocus();
		tt = new TimeThread();
		tt.start();
	}

	private void addLis() {
		button.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				tt.stopp();
				setNum();
				flush();
				ifCanMove = true;
				minute = 0;
				second = 0;
				nowCount = 0;
				count.setText("��ǰ���� " + nowCount);
				tt = new TimeThread();
				tt.start();
				panel.requestFocus();
			}
		});
		panel.addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent e) {
				int a = e.getKeyCode();
				if (a == KeyEvent.VK_BACK_SPACE) {
					Node node = link.get();
					if (node != null) {
						for (int i = 0; i < 4; i++)
							for (int j = 0; j < 4; j++)
								num[i][j] = node.num[i][j];
						nowCount = node.count;
						flush();
					}
					return;
				}
				if (a != KeyEvent.VK_UP && a != KeyEvent.VK_DOWN && a != KeyEvent.VK_LEFT && a != KeyEvent.VK_RIGHT)
					return;

				if (!ifCanMove) {
					JOptionPane.showMessageDialog(frame, "���Ѿ���·����");
					return;
				}
				for (int i = 0; i < 4; i++)
					for (int j = 0; j < 4; j++)
						oldNum[i][j] = num[i][j];
				oldCo = nowCount;
				boolean isMove = false;
				if (a == KeyEvent.VK_UP)
					isMove = GeZiUp();
				else if (a == KeyEvent.VK_DOWN)
					isMove = GeZiDown();
				else if (a == KeyEvent.VK_LEFT)
					isMove = GeZiLeft();
				else if (a == KeyEvent.VK_RIGHT)
					isMove = GeZiRight();
				if (isMove) {
					addNum();
					link.set(new Node(oldNum, oldCo));
					flush();
				}
				if (!ifCanMove())
					JOptionPane.showMessageDialog(frame, "���Ѿ���·����");
			}

			private boolean GeZiRight() {
				boolean ifMoveRight = false;
				for (int i = 0; i < 4; i++)
					for (int j = 3; j > 0; j--)
						if ((num[i][j] == 0 && num[i][j - 1] != 0) || (num[i][j] == num[i][j - 1] && num[i][j] != 0))
							ifMoveRight = true;
				if (!ifMoveRight)
					return false;
				for (int i = 0; i < 4; i++) {
					for (int x = 0; x < 4; x++) {
						for (int j = 3; j > 0; j--) {
							if (num[i][j] == 0) {
								num[i][j] = num[i][j - 1];
								num[i][j - 1] = 0;
							}
						}
					}
					for (int j = 3; j > 0; j--) {
						if (num[i][j] == num[i][j - 1]) {
							nowCount += num[i][j];
							num[i][j] = num[i][j] * 2;
							num[i][j - 1] = 0;
						}
					}
					for (int x = 0; x < 4; x++) {
						for (int j = 3; j > 0; j--) {
							if (num[i][j] == 0) {
								num[i][j] = num[i][j - 1];
								num[i][j - 1] = 0;
							}
						}
					}
				}
				return true;
			}

			private boolean GeZiLeft() {
				boolean ifMoveLeft = false;
				for (int i = 0; i < 4; i++)
					for (int j = 0; j < 3; j++)
						if ((num[i][j] == 0 && num[i][j + 1] != 0) || (num[i][j] == num[i][j + 1]) && num[i][j] != 0)
							ifMoveLeft = true;
				if (!ifMoveLeft)
					return false;
				for (int i = 0; i < 4; i++) {
					for (int x = 0; x < 4; x++) {
						for (int j = 0; j < 3; j++) {
							if (num[i][j] == 0) {
								num[i][j] = num[i][j + 1];
								num[i][j + 1] = 0;
							}
						}
					}
					for (int j = 0; j < 3; j++) {
						if (num[i][j] == num[i][j + 1]) {
							nowCount += num[i][j];
							num[i][j] = num[i][j] * 2;
							num[i][j + 1] = 0;
						}
					}
					for (int x = 0; x < 4; x++) {
						for (int j = 0; j < 3; j++) {
							if (num[i][j] == 0) {
								num[i][j] = num[i][j + 1];
								num[i][j + 1] = 0;
							}
						}
					}
				}
				return true;
			}

			private boolean GeZiDown() {
				boolean ifMoveDown = false;
				for (int j = 0; j < 4; j++)
					for (int i = 3; i > 0; i--)
						if ((num[i][j] == 0 && num[i - 1][j] != 0) || (num[i][j] == num[i - 1][j] && num[i][j] != 0))
							ifMoveDown = true;
				if (!ifMoveDown)
					return false;
				for (int j = 0; j < 4; j++) {
					for (int x = 0; x < 4; x++) {
						for (int i = 3; i > 0; i--) {
							if (num[i][j] == 0) {
								num[i][j] = num[i - 1][j];
								num[i - 1][j] = 0;
							}
						}
					}
					for (int i = 3; i > 0; i--) {
						if (num[i][j] == num[i - 1][j]) {
							nowCount += num[i][j];
							num[i][j] = num[i][j] * 2;
							num[i - 1][j] = 0;
						}
					}
					for (int x = 0; x < 4; x++) {
						for (int i = 3; i > 0; i--) {
							if (num[i][j] == 0) {
								num[i][j] = num[i - 1][j];
								num[i - 1][j] = 0;
							}
						}
					}
				}
				return true;
			}

			private boolean GeZiUp() {
				boolean ifMoveUp = false;
				for (int j = 0; j < 4; j++)
					for (int i = 0; i < 3; i++)
						if ((num[i][j] == 0 && num[i + 1][j] != 0) || (num[i][j] == num[i + 1][j] && num[i][j] != 0))
							ifMoveUp = true;
				if (!ifMoveUp)
					return false;
				for (int j = 0; j < 4; j++) {
					for (int x = 0; x < 4; x++) {
						for (int i = 0; i < 3; i++) {
							if (num[i][j] == 0) {
								num[i][j] = num[i + 1][j];
								num[i + 1][j] = 0;
							}
						}
					}
					for (int i = 0; i < 3; i++) {
						if (num[i][j] == num[i + 1][j]) {
							nowCount += num[i][j];
							num[i][j] = num[i][j] * 2;
							num[i + 1][j] = 0;
						}
					}
					for (int x = 0; x < 4; x++) {
						for (int i = 0; i < 3; i++) {
							if (num[i][j] == 0) {
								num[i][j] = num[i + 1][j];
								num[i + 1][j] = 0;
							}
						}
					}
				}
				return true;
			}
		});
	}

	private void flush() {
		for (int i = 0; i < 16; i++) {
			Grids[i].update(num[i / 4][i % 4]);
		}
		if (nowCount > maxCo)
			maxCo = nowCount;
		count.setText("��ǰ���� " + nowCount);
		maxCount.setText("��߷�" + maxCo);
	}

	private void setNum() {
		num = new int[4][4];
		for (int i = 0; i < 4; i++)
			for (int j = 0; j < 4; j++)
				num[i][j] = 0;
		for (int i = 0; i < 5; i++)
			addNum();
		link.head = null;
		oldNum = new int[4][4];
		for (int i = 0; i < 4; i++)
			for (int j = 0; j < 4; j++)
				oldNum[i][j] = num[i][j];
	}

	private void addNum() {
		int m = (int) (Math.random() * 4);
		int n = (int) (Math.random() * 4);
		while (num[m][n] != 0) {
			m = (int) (Math.random() * 4);
			n = (int) (Math.random() * 4);
		}
		num[m][n] = 2;
	}

	private boolean ifCanMove() {
		if (!ifCanMove)
			return false;
		for (int i = 0; i < 4; i++)
			for (int j = 0; j < 4; j++)
				if (num[i][j] == 0)
					return true;
		for (int i = 0; i < 4; i++)
			for (int j = 0; j < 3; j++)
				if (num[i][j] == num[i][j + 1] || num[j][i] == num[j + 1][i])
					return true;
		ifCanMove = false;
		return ifCanMove;
	}

	public static void main(String[] args) {
		new Game2048_v3();
	}
}

class Grid3 {
	JLabel lab;

	public Grid3(int num) {
		lab = new JLabel();
		lab.setHorizontalAlignment(JLabel.CENTER);
		lab.setFont(new Font("����", Font.BOLD, 20));
		lab.setOpaque(true);
		set(num);
	}

	private void set(int num) {
		String str = num + "";
		if (num == 0)
			str = "";
		lab.setText(str);
		switch (num) {
		case 2:
			lab.setBackground(new Color(238, 228, 218));
			break;
		case 4:
			lab.setBackground(new Color(237, 224, 200));
			break;
		case 8:
			lab.setBackground(new Color(242, 177, 121));
			break;
		case 16:
			lab.setBackground(new Color(245, 149, 99));
			break;
		case 32:
			lab.setBackground(new Color(246, 124, 95));
			break;
		case 64:
			lab.setBackground(new Color(246, 94, 59));
			break;
		case 128:
			lab.setBackground(new Color(237, 207, 114));
			break;
		case 256:
			lab.setBackground(new Color(237, 204, 97));
			break;
		case 512:
			lab.setBackground(new Color(237, 200, 80));
			break;
		case 1024:
			lab.setBackground(new Color(237, 197, 63));
			break;
		case 2048:
			lab.setBackground(new Color(237, 194, 46));
			break;
		default:
			lab.setBackground(Color.WHITE);
		}
	}

	public void update(int num) {
		set(num);
	}

	public JLabel ret() {
		return lab;
	}
}

class Linked {
	Node head = null;

	public void set(Node node) {
		node.next = head;
		head = node;
	}

	public Node get() {
		Node ret = head;
		if (head != null)
			head = head.next;
		return ret;
	}
}

class Node {
	int[][] num = new int[4][4];
	int count = 0;
	Node next = null;

	public Node(int[][] n, int count) {
		for (int i = 0; i < 4; i++)
			for (int j = 0; j < 4; j++)
				num[i][j] = n[i][j];
		this.count = count;
	}
}
